﻿using System.Globalization;

namespace Microsoft.Xna.Framework.Graphics
{
	/// <summary>
	/// Defines the window dimensions of a render-target surface onto which a 3D volume projects.
	/// </summary>
    public struct Viewport
	{
		private float minDepth;
		private float maxDepth;

		#region Properties
		/// <summary>
		/// Gets the aspect ratio used by the viewport
		/// </summary>
		public float AspectRatio
		{
			get
			{
				if (this.Width != 0 && this.Height != 0)
				{
					return this.Width / this.Height;
				}
				return 0f;
			}
		}

		/// <summary>
		/// Gets or sets the height dimension of the viewport on the render-target surface, in pixels.
		/// </summary>
		public int Height;

		/// <summary>
		/// Gets or sets the maximum depth of the clip volume.
		/// </summary>
		public float MaxDepth
		{
			get { return maxDepth; }
			set { maxDepth = value; }
		}

		/// <summary>
		/// Gets or sets the minimum depth of the clip volume.
		/// </summary>
		public float MinDepth
		{
			get { return minDepth; }
			set { minDepth = value; }
		}

		/// <summary>
		/// Returns the title safe area of the current viewport.
		/// </summary>
		public Rectangle TitleSafeArea
		{
			get { return new Rectangle(this.X, this.Y, this.Width, this.Height); }
		}

		/// <summary>
		/// Gets or sets the width dimension of the viewport on the render-target surface, in pixels.
		/// </summary>
		public int Width;

		/// <summary>
		/// Gets or sets the pixel coordinate of the upper-left corner of the viewport on the render-target surface.
		/// </summary>
        public int X;

		/// <summary>
		/// Gets or sets the pixel coordinate of the upper-left corner of the viewport on the render-target surface.
		/// </summary>
        public int Y;
		#endregion

		#region Constructors
		/// <summary>
		/// Creates an instance of this object.
		/// </summary>
		/// <param name="bounds">A bounding box that defines the location and size of the viewport in a render target.</param>
		public Viewport(Rectangle bounds)
		{
			this.X = bounds.X;
			this.Y = bounds.Y;
			this.Width = bounds.Width;
			this.Height = bounds.Height;
			this.maxDepth = 1f;
			this.minDepth = 0f;
		}

		/// <summary>
		/// Creates an instance of this object.
		/// </summary>
		/// <param name="x">The x coordinate of the upper-left corner of the viewport in pixels.</param>
		/// <param name="y">The y coordinate of the upper-left corner of the viewport in pixels.</param>
		/// <param name="width">The width of the viewport in pixels.</param>
		/// <param name="height">The height of the viewport in pixels.</param>
		public Viewport(int x, int y, int width, int height)
		{
			this.X = x;
			this.Y = y;
			this.Width = width;
			this.Height = height;
			this.maxDepth = 1f;
			this.minDepth = 0f;
		}
		#endregion

		#region Private Methods
		bool WithinEpsilon(float a, float b)
		{
			float num = a - b;
			return ((-1.401298E-45f <= num) && (num <= 1.401298E-45f));
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Projects a 3D vector from object space into screen space.
		/// </summary>
		/// <param name="source">The vector to project.</param>
		/// <param name="projection">The projection matrix.</param>
		/// <param name="view">The view matrix.</param>
		/// <param name="world">The world matrix.</param>
		public Vector3 Project(Vector3 source, Matrix projection, Matrix view, Matrix world)
        {
            Matrix matrix = Matrix.Multiply(Matrix.Multiply(world, view), projection);
			Vector3 vector = Vector3.Transform(source, matrix);
			float a = (((source.X * matrix.M14) + (source.Y * matrix.M24)) + (source.Z * matrix.M34)) + matrix.M44;
			if (!WithinEpsilon(a, 1))
			{
				vector = (Vector3) (vector / a);
			}
			vector.X = (((vector.X + 1) * 0.5f) * Width) + X;
			vector.Y = (((-vector.Y + 1) * 0.5f) * Height) + Y;
			vector.Z = (vector.Z * (MaxDepth - MinDepth)) + MinDepth;
			return vector;
        }

		/// <summary>
		/// Retrieves a string representation of this object.
		/// </summary>
		public override string ToString()
		{
			return string.Format(CultureInfo.CurrentCulture, "{{X:{0} Y:{1} Width:{2} Height:{3} MinDepth:{4} MaxDepth:{5}}}", this.X, this.Y, this.Width, this.Height, this.MinDepth, this.MaxDepth);
		}

		/// <summary>
		/// Converts a screen space point into a corresponding point in world space.
		/// </summary>
		/// <param name="source">The vector to project.</param>
		/// <param name="projection">The projection matrix.</param>
		/// <param name="view">The view matrix.</param>
		/// <param name="world">The world matrix.</param>
        public Vector3 Unproject(Vector3 source, Matrix projection, Matrix view, Matrix world)
        {
            Vector3 position;
			Matrix matrix = Matrix.Invert(Matrix.Multiply(Matrix.Multiply(world, view), projection));
			position.X = (((source.X - X) / ((float)Width)) * 2) - 1;
			position.Y = -((((source.Y - Y) / ((float)Height)) * 2) - 1);
			position.Z = (source.Z - MinDepth) / (MaxDepth - MinDepth);
			position = Vector3.Transform(position, matrix);
			float a = (((source.X * matrix.M14) + (source.Y * matrix.M24)) + (source.Z * matrix.M34)) + matrix.M44;
			if (!WithinEpsilon(a, 1))
			{
				position = (position / a);
			}
			return position;
		}
		#endregion
	}
}
